#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#define DEFAULT_PORT                    "6325"
#define MAXBUF                                  1024
#define DEFAULT_HOST                    "localhost"
#define MAX_INPUT_SIZE                  32
#define NUM_OF_HEAPS                    4
#define IS_MISERE                               '1'
#define IS_NOT_MISERER                  '0'
#define CLIENT_WIN                              'C'
#define OTHER_WIN                               'O'
#define CLIENT_WIN_AND_TURN             'S'
#define OTHER_WIN_AND_TURN              'G'
#define WATCHER_WIN                             'K'
#define ILLEGAL_MOVE                    'I'
#define LEGAL_MOVE                              'L'
#define QUIT_MOVE                               'Q'
#define INVALID_DESTINATION             'D'
#define ACCEPT_VIEWER                   'V'
#define ACCEPT_PLAYER                   'P'
#define CLIENT_TURN                             'T'
#define NOT_CLIENT_TURN                                         'N'
#define WATCH_MODE                              'W'
#define PROMOTION                               'Z'
#define MESSAGE                                 'M'
#define TOO_MANY_PLAYERS                                                'A'
#define IS_PLAYER                               1
#define IS_WATCHER                              0

#define IS_PLAYER_TURN(BUFFER)  *((int *) (BUFFER+ 1 + (4 * sizeof(int)))) == 1

void printHeapSizes(char *messageBuffer) {
	/*
	 * Prints the heap sizes from the received buffer
	 * to the screen.
	 */
	int i, currVal;
	printf("Heap sizes are ");
	for (i = 0; i < NUM_OF_HEAPS; ++i) {
		currVal = *((int *) (messageBuffer + 1 + (i * sizeof(int))));
		if (i != NUM_OF_HEAPS - 1) {
			printf("%d, ", currVal);
		} else {
			printf("%d\n", currVal);
		}
	}
}

int validateUserInput(char *userInput) {
	/*
	 * Validate the user input.
	 *
	 * Returns the amount the user inputed
	 * Unless the actions is Q in which case
	 * returns 0
	 *
	 * Returns -1 on invalid input
	 */
	int amount, len;
	char *s;
	len = strlen(userInput);
	if ((len < 1)
			|| (len == 2
					&& ((userInput[0] != QUIT_MOVE) || userInput[1] != '\n'))
			|| (len > 2 && userInput[1] != ' ')) {
		return -1;
	}

	if (userInput[0] < 'A' || userInput[0] > 'A' + NUM_OF_HEAPS
			|| userInput[1] != ' ') {
		return -1;
	}

	amount = strtol(userInput + 2, &s, 10);

	if (*s != '\n') {
		return -1;
	}

	return amount;
}

int handleFirstMessage(char *messageBuffer, int* myId) {
	/*
	 * Prints the Game definitions from the received buffer
	 * to the screen.
	 */
	int currVal;
	printf("Number of players is ");
	currVal = *((int *) (messageBuffer + 1 + (5 * sizeof(int))));
	printf("%d\n", currVal);

	printf("You are client ");
	currVal = *((int *) (messageBuffer + 1 + (6 * sizeof(int))));
	*myId = currVal;
	printf("%d\n", currVal);

	//player vs viewer
	currVal = *((int *) (messageBuffer + 1 + (7 * sizeof(int))));
	if (currVal == IS_PLAYER) {
		printf("You are playing\n");
		return 1;
	} else {
		printf("You are only viewing\n");
		return 0;
	}
}

void handleMessag(char *buffer) {
	printf("%c: ", buffer[4]);
	printf("%s", buffer + 6);
}

int main(int argc, char**argv) {
	int portnum;
	int server_sock;
	int printHeaps;
	int endOfGame = 0;
	int i;
	int fdmax;
	int pendingRequests;
	int msgLength;
	struct addrinfo hints, *res;
	char *port;
	char *s;
	char buffer[MAXBUF];
	char *host_address;
	char outputMessage[sizeof(char) + sizeof(int)];
	int clientNo;
	fd_set master_set;
	fd_set read_fds;

	if (argc > 3) {
		printf("usage: nim [hostname [port]]\n");
		exit(1);
	}

	if (argc > 1) {
		host_address = argv[1];
	} else {
		host_address = DEFAULT_HOST;
	}

	if (argc > 2) {
		portnum = strtol(argv[2], &s, 10);
		//stdin
		//Check validity of port
		if (strlen(s) != 0) {
			printf("Incorrect value of port ");
			exit(1);
		}

		//Check if port is in the correct range
		if (portnum < 0 || portnum > 65535) {
			printf("port is not in the valid ports range!");
			exit(1);
		}

		port = argv[2];
	} else {
		port = DEFAULT_PORT;
	}

	// Create socket
	bzero(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;

	if (getaddrinfo(host_address, port, &hints, &res) != 0) {
		printf("Couldn't get the address info from the host"
				" provided.\n");
		exit(1);
	}

	if ((server_sock = socket(res->ai_family, res->ai_socktype,
			res->ai_protocol)) < 0) {
		perror("Socket");
		exit(errno);
	}

	if (connect(server_sock, res->ai_addr, res->ai_addrlen) != 0) {
		close(server_sock);
		perror("Socket");
		exit(errno);
	}

	// Initialize fd sets.
	FD_ZERO(&master_set);
	FD_ZERO(&read_fds);

	// Insert the server socket to the fd set
	FD_SET(server_sock, &master_set);
	// Also inset stdin to the fd set
	FD_SET(STDIN_FILENO, &master_set);

	fdmax = server_sock + 1;

	while (1) {

		//not beginning of game

		printHeaps = 1;

		read_fds = master_set;

		// Receive a message from the server.
		if ((pendingRequests = select(fdmax + 1, &read_fds, NULL, NULL, NULL))
				== -1) {
			perror("select");
			exit(errno);
		}
		for (i = 0; i < fdmax + 1 && pendingRequests > 0; ++i) {

			if (FD_ISSET(i, &read_fds)) {
				--pendingRequests;

				// Handle input from user
				if (i == STDIN_FILENO) {
					if ((msgLength = read(i, buffer, MAXBUF)) == -1) {
						perror("read error");
						exit(errno);
					}
					buffer[msgLength] = '\0';
					// Handle quit move
					if (msgLength == 2 && buffer[0] == QUIT_MOVE) {
						close(server_sock);
						exit(0);
						// Handle message sending
					} else if (strncmp(buffer, "MSG", 3) == 0
							&& sscanf(buffer, "MSG %d ", &clientNo) == 1) {
						if (send(server_sock, buffer, MAXBUF, 0) < 0) {
							close(server_sock);
							perror("Send");
							exit(errno);
						}
						continue;
						// A players turn
					} else if (validateUserInput(buffer) != -1) {
						// Pack the input and send it to the server
						outputMessage[0] = buffer[0];

						// pack the amount to reduce from the heap
						*((int *) (outputMessage + 1)) = strtol(&buffer[2], &s,
								10);

						if (send(server_sock, outputMessage,
								sizeof(outputMessage), 0) < 0) {
							close(server_sock);
							perror("Send");
							exit(errno);
						}
						// not a valid message
					} else {

					}
				}

				// Handle input from nim server
				else {
					if (!recv(i, buffer, MAXBUF, 0)) {
						printf("Disconnected from server\n");
						close(server_sock);
						exit(0);
					}

					switch (buffer[0]) {
					case TOO_MANY_PLAYERS:
						printf(
								"Client rejected: too many clients are already connected\n");
						exit(0);
					case IS_MISERE:
						printf("This is a Misere game\n");
						break;
					case IS_NOT_MISERER:
						printf("This is a regular game\n");
						break;
					case PROMOTION:
						printf("You are now playing!\n");
						continue;
						break;
					case WATCH_MODE:
						break;
					case CLIENT_TURN:
						break;
					case LEGAL_MOVE:
						printf("Move accepted\n");
						break;
					case ILLEGAL_MOVE:
						printf("Illegal move\n");
						break;
					case CLIENT_WIN:
						endOfGame = 1;
						break;
					case OTHER_WIN:
						endOfGame = 1;
						break;
					case WATCHER_WIN:
						endOfGame = 1;
						break;
					case CLIENT_WIN_AND_TURN:
						printf("Move accepted\n");
						endOfGame = 1;
						break;
					case OTHER_WIN_AND_TURN:
						printf("Move accepted\n");
						endOfGame = 1;
						break;
					case MESSAGE:
						handleMessag(buffer);
						continue;
						break;
					case NOT_CLIENT_TURN:
						printHeaps = 0;
						printf("Move rejected: this is not your turn\n");
						break;
					case INVALID_DESTINATION:
						printHeaps = 0;
						printf("Incorrect message destination!\n");
						break;
					case QUIT_MOVE:
						printHeaps = 0;
						break;
					default:
						printf("Illegal message from the server!"
								" This is UNACCEPTABLE!\n");
					}

					if (printHeaps) {
						printHeapSizes(buffer);
					}

					if (endOfGame) {
						if (buffer[0] == CLIENT_WIN
								|| buffer[0] == CLIENT_WIN_AND_TURN) {
							printf("You win!\n");
						} else if (buffer[0] == OTHER_WIN
								|| buffer[0] == OTHER_WIN_AND_TURN) {
							printf("You lose!\n");
						} else {
							printf("Game over!\n");
						}
						break;
					}

					// Get the next move from the user
					if (IS_PLAYER_TURN(buffer)) {

						printf("Your turn:\n");
						continue;
					}
				}
			}
		}
	}

	//close the server
	close(server_sock);

	return 0;
}

#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#define DEFAULT_PORT                    "6325"
#define MAXBUF                                  1024
#define DEFAULT_HOST                    "localhost"
#define MAX_INPUT_SIZE                  32
#define NUM_OF_HEAPS                    4
#define IS_MISERE                               '1'
#define IS_NOT_MISERER                  '0'
#define CLIENT_WIN                              'C'
#define OTHER_WIN                               'O'
#define CLIENT_WIN_AND_TURN             'S'
#define OTHER_WIN_AND_TURN              'G'
#define WATCHER_WIN                             'K'
#define ILLEGAL_MOVE                    'I'
#define LEGAL_MOVE                              'L'
#define QUIT_MOVE                               'Q'
#define INVALID_DESTINATION             'D'
#define ACCEPT_VIEWER                   'V'
#define ACCEPT_PLAYER                   'P'
#define CLIENT_TURN                             'T'
#define NOT_CLIENT_TURN                                         'N'
#define WATCH_MODE                              'W'
#define PROMOTION                               'Z'
#define MESSAGE                                 'M'
#define TOO_MANY_PLAYERS                                                'A'
#define IS_PLAYER                               1
#define IS_WATCHER                              0

#define IS_PLAYER_TURN(BUFFER)  *((int *) (BUFFER+ 1 + (4 * sizeof(int)))) == 1

void printHeapSizes(char *messageBuffer) {
        /*
         * Prints the heap sizes from the received buffer
         * to the screen.
         */
        int i, currVal;
        printf("Heap sizes are ");
        for (i = 0; i < NUM_OF_HEAPS; ++i) {
                currVal = *((int *) (messageBuffer + 1 + (i * sizeof(int))));
                if (i != NUM_OF_HEAPS - 1) {
                        printf("%d, ", currVal);
                } else {
                        printf("%d\n", currVal);
                }
        }
}

int validateUserInput(char *userInput) {
        /*
         * Validate the user input.
         *
         * Returns the amount the user inputed
         * Unless the actions is Q in which case
         * returns 0
         *
         * Returns -1 on invalid input
         */
        int amount, len;
        char *s;
        len = strlen(userInput);
        if ((len < 1)
                        || (len == 2
                                        && ((userInput[0] != QUIT_MOVE) || userInput[1] != '\n'))
                        || (len > 2 && userInput[1] != ' ')) {
                return -1;
        }

        if (userInput[0] < 'A' || userInput[0] > 'A' + NUM_OF_HEAPS
                        || userInput[1] != ' ') {
                return -1;
        }

        amount = strtol(userInput + 2, &s, 10);

        if (*s != '\n') {
                return -1;
        }

        return amount;
}

int handleFirstMessage(char *messageBuffer, int* myId) {
        /*
         * Prints the Game definitions from the received buffer
         * to the screen.
         */
        int currVal;
        printf("Number of players is ");
        currVal = *((int *) (messageBuffer + 1 + (5 * sizeof(int))));
        printf("%d\n", currVal);

        printf("You are client ");
        currVal = *((int *) (messageBuffer + 1 + (6 * sizeof(int))));
        *myId = currVal;
        printf("%d\n", currVal);

        //player vs viewer
        currVal = *((int *) (messageBuffer + 1 + (7 * sizeof(int))));
        if (currVal == IS_PLAYER) {
                printf("You are playing\n");
                return 1;
        } else {
                printf("You are only viewing\n");
                return 0;
        }
}

void handleMessag(char *buffer) {
        printf("%c: ", buffer[4]);
        printf("%s", buffer + 6);
}

int main(int argc, char**argv) {
        int portnum;
        int server_sock;
        int printHeaps;
        int endOfGame = 0;
        int i;
        int fdmax;
        int pendingRequests;
        int msgLength;
        struct addrinfo hints, *res;
        char *port;
        char *s;
        char buffer[MAXBUF];
        char *host_address;
        char outputMessage[sizeof(char) + sizeof(int)];
        int clientNo;
        fd_set master_set;
        fd_set read_fds;

        if (argc > 3) {
                printf("usage: nim [hostname [port]]\n");
                exit(1);
        }

        if (argc > 1) {
                host_address = argv[1];
        } else {
                host_address = DEFAULT_HOST;
        }

        if (argc > 2) {
                portnum = strtol(argv[2], &s, 10);
                //stdin
                //Check validity of port
                if (strlen(s) != 0) {
                        printf("Incorrect value of port ");
                        exit(1);
                }

                //Check if port is in the correct range
                if (portnum < 0 || portnum > 65535) {
                        printf("port is not in the valid ports range!");
                        exit(1);
                }

                port = argv[2];
        } else {
                port = DEFAULT_PORT;
        }

        // Create socket
        bzero(&hints, sizeof(hints));
        hints.ai_family = AF_INET;
        hints.ai_socktype = SOCK_STREAM;

        if (getaddrinfo(host_address, port, &hints, &res) != 0) {
                printf("Couldn't get the address info from the host"
                                " provided.\n");
                exit(1);
        }

        if ((server_sock = socket(res->ai_family, res->ai_socktype,
                        res->ai_protocol)) < 0) {
                perror("Socket");
                exit(errno);
        }

        if (connect(server_sock, res->ai_addr, res->ai_addrlen) != 0) {
                close(server_sock);
                perror("Socket");
                exit(errno);
        }

        // Initialize fd sets.
        FD_ZERO(&master_set);
        FD_ZERO(&read_fds);

        // Insert the server socket to the fd set
        FD_SET(server_sock, &master_set);
        // Also inset stdin to the fd set
        FD_SET(STDIN_FILENO, &master_set);

        fdmax = server_sock + 1;


        while (1) {

                //not beginning of game

                printHeaps = 1;

                read_fds = master_set;

                // Receive a message from the server.
                if ((pendingRequests = select(fdmax + 1, &read_fds, NULL, NULL, NULL))
                                == -1) {
                        perror("select");
                        exit(errno);
                }
                for (i = 0; i < fdmax + 1 && pendingRequests > 0; ++i) {

                        if (FD_ISSET(i, &read_fds)) {
                                --pendingRequests;

                                // Handle input from user
                                if (i == STDIN_FILENO) {
                                        if ((msgLength = read(i, buffer, MAXBUF)) == -1) {
                                                perror("read error");
                                                exit(errno);
                                        }
                                        buffer[msgLength] = '\0';
                                        // Handle quit move
                                        if (msgLength == 2 && buffer[0] == QUIT_MOVE) {
                                                close(server_sock);
                                                exit(0);
                                                // Handle message sending
                                        } else if (strncmp(buffer, "MSG", 3) == 0
                                                        && sscanf(buffer, "MSG %d ", &clientNo) == 1) {
                                                if (send(server_sock, buffer, MAXBUF, 0) < 0) {
                                                        close(server_sock);
                                                        perror("Send");
                                                        exit(errno);
                                                }
                                                continue;
                                                // A players turn
                                        } else if (validateUserInput(buffer)!=-1) {
                                                // Pack the input and send it to the server
                                                outputMessage[0] = buffer[0];

                                                // pack the amount to reduce from the heap
                                                *((int *) (outputMessage + 1)) = strtol(&buffer[2], &s,
                                                                10);

                                                if (send(server_sock, outputMessage,
                                                                sizeof(outputMessage), 0) < 0) {
                                                        close(server_sock);
                                                        perror("Send");
                                                        exit(errno);
                                                }
                                                // not a valid message
                                        } else {

                                        }
                                }

                                // Handle input from nim server
                                else {
                                        if (!recv(i, buffer, MAXBUF, 0)) {
                                                printf("Disconnected from server\n");
                                                close(server_sock);
                                                exit(0);
                                        }

                                        switch (buffer[0]) {
                                        case TOO_MANY_PLAYERS:
                                                printf(
                                                                "Client rejected: too many clients are already connected\n");
                                                exit(0);
                                        case IS_MISERE:
                                                printf("This is a Misere game\n");
                                                break;
                                        case IS_NOT_MISERER:
                                                printf("This is a regular game\n");
                                                break;
                                        case PROMOTION:
                                                printf("You are now playing!\n");
                                                continue;
                                                break;
                                        case WATCH_MODE:
                                                break;
                                        case CLIENT_TURN:
                                                break;
                                        case LEGAL_MOVE:
                                                printf("Move accepted\n");
                                                break;
                                        case ILLEGAL_MOVE:
                                                printf("Illegal move\n");
                                                break;
                                        case CLIENT_WIN:
                                                endOfGame = 1;
                                                break;
                                        case OTHER_WIN:
                                                endOfGame = 1;
                                                break;
                                        case WATCHER_WIN:
                                                endOfGame = 1;
                                                break;
                                        case CLIENT_WIN_AND_TURN:
                                                printf("Move accepted\n");
                                                endOfGame = 1;
                                                break;
                                        case OTHER_WIN_AND_TURN:
                                                printf("Move accepted\n");
                                                endOfGame = 1;
                                                break;
                                        case MESSAGE:
                                                handleMessag(buffer);
                                                continue;
                                                break;
                                        case NOT_CLIENT_TURN:
                                                printHeaps = 0;
                                                printf("Move rejected: this is not your turn\n");
                                                break;
                                        case INVALID_DESTINATION:
                                                printHeaps = 0;
                                                printf("Incorrect message destination!\n");
                                                break;
                                        case QUIT_MOVE:
                                                printHeaps = 0;
                                                break;
                                        default:
                                                printf("Illegal message from the server!"
                                                                " This is UNACCEPTABLE!\n");
                                        }

                                        if (printHeaps) {
                                                printHeapSizes(buffer);
                                        }

                                        if (endOfGame) {
                                                if (buffer[0] == CLIENT_WIN
                                                                || buffer[0] == CLIENT_WIN_AND_TURN) {
                                                        printf("You win!\n");
                                                } else if (buffer[0] == OTHER_WIN
                                                                || buffer[0] == OTHER_WIN_AND_TURN) {
                                                        printf("You lose!\n");
                                                } else {
                                                        printf("Game over!\n");
                                                }
                                                break;
                                        }

                                        // Get the next move from the user
                                        if (IS_PLAYER_TURN(buffer)) {

                                                printf("Your turn:\n");
                                                continue;
                                        }
                                }
                        }
                }
        }

        //close the server
        close(server_sock);

        return 0;
}
